#include <iostream>
#include <conio.h>

#include <QtCore/QCoreApplication>
#include <QJsonDocument>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDriver>
#include <QJsonObject>
#include <QDebug>

#include "../include/Constance.h"

#include "RPHandler/UploadHandler.h"
#include "RPHandler/UserHandler.h"
#include "RPHandler/EvaluationHandler.h"

#include "Manager/UserManager.h"

#include "SslServer.h"
#include "Cpu.h"
#include "TCServerCore.h"
#include "Store/Database.h"
#include "Store/DatabaseException.h"
#include "RPHandler/DiscoverHandler.h"

using namespace std;
using namespace TC;
using namespace TCServer;

QString toJson(const QVariantMap &variantMap)
{
	return QJsonDocument(QJsonObject::fromVariantMap(variantMap))
		.toJson(QJsonDocument::JsonFormat::Indented);
}

TCServerCore::TCServerCore(int argc, char *argv[])
	: app(argc, argv)
{
}

TCServerCore::~TCServerCore()
{
}



int TCServerCore::run()
{
    try
    {
        return server_process();
    }
    catch (std::exception e)
    {
        qFatal("Program Fatal Error : %s", e.what());
    }
    return -1;
}

int TCServerCore::server_process()
{
    QStringList arguments = app.arguments();
// 	if (arguments.contains("--delete-database"))
// 	{
// 		auto res = Store::Database::instance()->dropDatabase();
// 		qDebug().noquote() << "drop database result : " << res << endl;
// 		return !res;
// 	}
	if (arguments.contains("--create-database"))
	{
		auto res = Store::Database::instance()->createDatabase();
		qDebug().noquote() << "create database result : " << res << endl;
		return !res;
	}

	QLocale::setDefault(QLocale(QLocale::Chinese, QLocale::China));

	// SSL check
	qDebug() << "SSL support: " << QSslSocket::supportsSsl() << endl;

	// Database connection
	Store::Database::instance();

	// RPHanlder
	Cpu::instance();
	RPHandler::UserHandler::instance();
	RPHandler::UploadHandler::instance();
	RPHandler::EvaluationHandler::instance();
	RPHandler::DiscoverHandler::instance();

	// Socket Handler
	SslServer *server = new SslServer;
	server->listen(QHostAddress::Any, Constance::numbers.port);
	cout << "Server is listening on port " << server->serverPort() << endl;

	QObject::connect(server, &SslServer::newConnection, [=]()
	{
		QSslSocket *s = server->nextPendingConnection();

		QObject::connect(s, &QSslSocket::encrypted, [=]()
		{
		});
		QObject::connect(s, &QSslSocket::readyRead, [=]()
		{
			auto data = s->readAll();
			QDataStream stream(&data, QIODevice::ReadOnly);
			QString command;
			QVariantMap args;
			try
			{
				stream >> command >> args;
				qDebug() << QDateTime::currentDateTime().toString()
					<< ", host : " << s->peerAddress().toString() << ", command : " << command;
				qDebug().noquote() << "with args : " << toJson(args) << endl;

				auto i = args.insert("extra.ip", s->peerAddress().toString());
				Cpu::instance()->onMessage(s, command, args);
			}
            catch (const Store::DatabaseException &e)
            {
                qCritical() << "<Error>[" << QDateTime::currentDateTime().toString() << "]"
                    << "DatabaseException(" << e.error.type() << ") when executing user command : " << command << endl;
                qCritical() << "   args : " << toJson(args) << endl;
                qCritical() << "   DatabaseException : " << e.what() << endl;

                if(e.error.type() == QSqlError::ErrorType::ConnectionError)
                {
                    qWarning() << "Maybe connection is closed, reconnecting ..." << endl;
                    if (!Store::Database::instance()->reconnect())
                    {
                        qCritical() << "Reopen connection failed!" << endl;
                    }
                }
            }
			catch (const std::exception& e)
			{
				qCritical() << "<Error>[" << QDateTime::currentDateTime().toString() << "]"
					<< "error when executing user command : " << command << endl;
				qCritical() << "   args : " << toJson(args) << endl;
				qCritical() << "   std::exception : " << e.what() << endl;
			}
			catch (...)
			{
				qCritical() << "error when executing user command : " << command << endl;
				qCritical() << "   args : " << toJson(args) << endl;
				qCritical() << "   unknown exception " << endl;
			}
			s->close();
		});
		QObject::connect(s, static_cast<void(QSslSocket::*)(const QList<QSslError> &)>(&QSslSocket::sslErrors), [=](const QList<QSslError> &errors)
		{
            qDebug() << "SSL Errors : ";
            for (auto e : errors)
            {
                qDebug() << "    " << e.errorString();
            }
            qDebug() << endl;
		});
		QObject::connect(s, static_cast<void(QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error), [=]()
		{
			qDebug() << "error : " << s->errorString();
		});
		QObject::connect(s, &QSslSocket::disconnected, [=]()
		{
			s->deleteLater();
		});
	});

	return app.exec();
}